// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#include <sstream>

#include "Rules/ExtractedInfo.h"

namespace CangjieMigrator::Rules {

using Cangjie::AST::Node;

ExtractedInfo::ExtractedInfo(PositionRange replacePosition, const Node &node)
    : replacePosition(std::move(replacePosition)), node(node)
{}

Argument::Argument(const PositionRange &range): value(range) {}

Argument::Argument(const std::string &str): value(str) {}

bool Argument::IsRange() const
{
    return std::holds_alternative<PositionRange>(value);
}

bool Argument::IsString() const
{
    return std::holds_alternative<std::string>(value);
}

const PositionRange &Argument::GetRange() const
{
    return std::get<PositionRange>(value);
}

const std::string &Argument::GetString() const
{
    return std::get<std::string>(value);
}


template <typename, typename = void>
struct HasToString : std::false_type {};

template <typename T>
struct HasToString<T, std::void_t<decltype(std::declval<const T&>().ToString())>> : std::true_type {};

template <typename T>
static std::enable_if_t<HasToString<T>::value, std::string> FormatField(const std::string& name, const T& value)
{
    return "\n" + name + ": " + value.ToString();
}

template <typename T>
static std::enable_if_t<!HasToString<T>::value, std::string> FormatField(const std::string& name, const T& value)
{
    std::ostringstream oss;
    oss << "\n" << name << ": " << value;
    return oss.str();
}

CallExprExtractedInfo::CallExprExtractedInfo(
    PositionRange replacePosition, const Node &node, const std::string &funcName,
    const std::string &packageAndIdentifier, const std::string &funcType,
    const PositionRange &callerPositionRange, std::vector<Argument> arguments)
    : ExtractedInfo(std::move(replacePosition), node),
    funcName(funcName), packageAndIdentifier(packageAndIdentifier),
    funcType(funcType), callerPositionRange(callerPositionRange), arguments(std::move(arguments))
{}

MatchRule::Type CallExprExtractedInfo::GetType() const
{
    return MatchRule::Type::CallExpr;
}

std::string CallExprExtractedInfo::ToString() const
{
    auto oss = std::ostringstream();
    oss << "CallExprExtractedInfo";
    oss << FormatField("replacePosition", replacePosition);
    oss << FormatField("funcName", funcName);
    oss << FormatField("packageAndIdentifier", packageAndIdentifier);
    oss << FormatField("funcType", funcType);
    oss << FormatField("callerPositionRange", callerPositionRange);

    for (size_t i = 0; i < arguments.size(); i++) {
        oss << "\narguments[" << i << "]: ";
        if (arguments[i].IsRange()) {
            oss << arguments[i].GetRange().ToString();
        } else {
            oss << arguments[i].GetString();
        }
    }

    return oss.str();
}

MemberAccessExtractedInfo::MemberAccessExtractedInfo(PositionRange replacePosition, const Node &node,
    std::string member,
    std::string packageAndIdentifier,
    PositionRange memberPositionRange)
    : ExtractedInfo(std::move(replacePosition), node),
    member(std::move(member)),
    packageAndIdentifier(std::move(packageAndIdentifier)),
    memberPositionRange(std::move(memberPositionRange))
{}

MatchRule::Type MemberAccessExtractedInfo::GetType() const
{
    return MatchRule::Type::MemberAccess;
}

std::string MemberAccessExtractedInfo::ToString() const
{
    auto oss = std::ostringstream();
    oss << "MemberAccessExtractedInfo";
    oss << FormatField("replacePosition", replacePosition);
    oss << FormatField("member", member);
    oss << FormatField("packageAndIdentifier", packageAndIdentifier);
    oss << FormatField("memberPositionRange", memberPositionRange);
    return oss.str();
}

RefExprExtractedInfo::RefExprExtractedInfo(PositionRange replacePosition, const Node &node,
    std::string package, std::string identifier)
    : ExtractedInfo(std::move(replacePosition), node),
    package(std::move(package)), identifier(std::move(identifier))
{}

MatchRule::Type RefExprExtractedInfo::GetType() const
{
    return MatchRule::Type::RefExpr;
}

std::string RefExprExtractedInfo::ToString() const
{
    auto oss = std::ostringstream();
    oss << "RefExprExtractedInfo";
    oss << FormatField("replacePosition", replacePosition);
    oss << FormatField("package", package);
    oss << FormatField("identifier", identifier);
    return oss.str();
}

TypeNameExtractedInfo::TypeNameExtractedInfo(PositionRange replacePosition, const Node &node,
    std::string package, std::string typeName)
    : ExtractedInfo(std::move(replacePosition), node),
    package(std::move(package)), typeName(std::move(typeName))
{}

MatchRule::Type TypeNameExtractedInfo::GetType() const
{
    return MatchRule::Type::TypeName;
}

std::string TypeNameExtractedInfo::ToString() const
{
    auto oss = std::ostringstream();
    oss << "TypeNameExtractedInfo";
    oss << FormatField("replacePosition", replacePosition);
    oss << FormatField("package", package);
    oss << FormatField("typeName", typeName);
    return oss.str();
}

LetAsPatternExtractedInfo::LetAsPatternExtractedInfo(PositionRange replacePosition, const Node &node)
    : ExtractedInfo(std::move(replacePosition), node)
{}

MatchRule::Type LetAsPatternExtractedInfo::GetType() const
{
    return MatchRule::Type::LetAsPattern;
}

std::string LetAsPatternExtractedInfo::ToString() const
{
    auto oss = std::ostringstream();
    oss << "LetAsPatternExtractedInfo";
    oss << FormatField("replacePosition", replacePosition);
    return oss.str();
}

} //  namespace CangjieMigrator::Rules
